/*
Copyright 2011 Mary Suárez Machado

Este programa es de software libre: usted puede redistribuirlo y/o modificarlo según los términos de la Licencia de Gran pública de GNU como publicado por la Fundación de Software Libre, versión 3 de la Licencia, (o en su opción) cualquier versión posterior.

 Este programa es distribuido en la esperanza que será útil, pero SIN CUALQUIER GARANTÍA; sin aún la garantía implícita de VALOR COMERCIAL O SALUD PARA UN OBJETIVO PARTICULAR. Mirar la Licencia de Gran pública de GNU para más detalles.
*/


 /**
 *@file GunLZcoder.cpp
 *@brief Implementaciones de las funciones del módulo GunLZcoder
 *@author Mary Carmen Suarez
 *
 */

#include <iostream>
#include <fstream>
#include <cassert>
#include <cstdlib>
#include <GunLZcoder.h>

using namespace std;

GunLZcoder :: GunLZcoder ()
{
	leidos = 3; //Para saltarnos el 1 1 0
}

GunLZcoder :: GunLZcoder (const VectorBits & orig) : vb(orig)
{
	leidos = 3; //Para saltarnos el 1 1 0
}
 
GunLZcoder :: ~GunLZcoder ()
{}

int GunLZcoder :: SimboloHuffman ()
{
	VectorBits bloque;	
	int simbolo = 0;
	bool dato;

	//Cojemos siete bits
	for (int i=0; i<7; i++)
	{
		dato = vb.GetVector(leidos);
		bloque += dato;
		leidos++;
	}
	//Si estan en la tabla
	if ( hlc.EstaCodificacion(bloque) )
	{
		//Devolvemos su simbolo
		simbolo = hlc.GetSimbolo(bloque);	
		return simbolo;
	}
	else 
	{	
		//Cojemos otro bits (8 bits)
		dato = vb.GetVector(leidos);
		bloque += dato;
		leidos++;
		//Si estan el la tabla
		if ( hlc.EstaCodificacion(bloque) )
		{
			//Devolvemos su simbolo
			simbolo = hlc.GetSimbolo(bloque);
			return simbolo;
		}
		else
		{
			//Cojemos el ultimo bits (9 bits-maximo)
			dato = vb.GetVector(leidos);
			bloque += dato;
			leidos++;
			//Si esta en la tabla
			if ( hlc.EstaCodificacion(bloque) )
			{
				//Devolvemos su simbolo
				simbolo = hlc.GetSimbolo(bloque);
				return simbolo;	
			}
			else
			{
				cerr << "    "<<endl;
				exit (1);
			}
		}
	}
}

void GunLZcoder :: DescodificarSimbolo (int simbolo)
{
	bool dato;

	//Si el simbolo es un caracter lo almacenamos directamente en el vector
	if (simbolo < 256)
		vector_salida.AddCaracter( (unsigned char) simbolo );
	else //es una longitud
	{
		if (simbolo < 288)
		{
			//Calculamos el primer valor del rango de la tabla longitud
			int valor_rango = tl.PrimerRango(simbolo);
			//Y el numero de bits adicionales
			int nbits = tl.Nbits(simbolo);
			VectorBits longitud;
			//Cojemos los bits adiccionales (nbits)
			for (int i=0; i<nbits; i++)
			{
				dato = vb.GetVector(leidos);
				longitud += dato;
				leidos++;
			}
			//Y lo transformamos en entero
			int entero = longitud.NumeroEntero();
			//La longitud es el valor primero del rango mas ese entero
			int l=valor_rango+entero;
			//Ahora calculamos la distancia (despues de la longitud siempre hay una distancia)
			VectorBits dist;
			//Las distancias huffman son de 5 bits
			for (int i=0; i<5; i++)
			{
				dato = vb.GetVector(leidos);
				dist += dato;
				leidos++;
			}
			//Calculamos el simbolo al que corresponde dicha distancia
			int simb_dist = hd.GetSimbolo(dist);
			//Calculamos el primer valor del rango en el que esta comprendido dicho simbolo
			int rango_distancia = td.PrimerRango(simb_dist);
			//Y calculamos el numero de bits con el que se codifica
			int nbits_dist = td.NBits(simb_dist);

			VectorBits b_adic_dist;
			//Cojemos los bits adiccionales
			for (int i=0; i<nbits_dist; i++)
			{
				dato = vb.GetVector(leidos);
				b_adic_dist += dato;
				leidos++;
			}
			//Calculamos su entero
			int ent_dist = b_adic_dist.NumeroEntero ();
			//Y la distancia es el primer valor del rango mas ese entero
			int distancia = rango_distancia + ent_dist;

			//Desde esa distancia hasta esa longitud añadimos esos caracteres al vector
			int usados=vector_salida.GetNelem();
			int tope=(usados-distancia)+l;
			for (int i = usados-distancia; i < tope; i++)
				vector_salida.AddCaracter( vector_salida.GetCaracter(i) );
		}	
	}
}

void GunLZcoder :: Descodificacion (std::ostream & os)
{
	int simbolo=0;
	//Calculamos el primer codigo
	simbolo= SimboloHuffman();
	//Hasta que el codigo sea 256
	while (simbolo != 256)
	{
		//Decodificamos el codigo
		DescodificarSimbolo(simbolo);
		//Pedimos el siguiente
		simbolo= SimboloHuffman();
	}
	//En el fichero de salida escribimos el vector
	vector_salida.Escribir(os);
}

unsigned int GunLZcoder :: GetParidad () const
{
	return vector_salida.GetParidad();
}

unsigned int GunLZcoder :: Nelem () const
{
	return vector_salida.GetNelem();
}
